<template>
  <div class="kanban-container">
    <el-skeleton v-if="props.loading" animated :rows="6" />
    <el-empty v-else-if="!defects.length" description="暂无缺陷数据" />
    <el-scrollbar v-else class="kanban-scroll">
      <div class="kanban-board">
        <div
          v-for="column in columns"
          :key="column.key"
          class="kanban-column"
          :class="{ 
            'drag-target': isDragging && dragTargetColumn === column.key && draggingColumn !== column.key,
            'drag-source': isDragging && draggingColumn === column.key,
            'drag-over': isDragging && dragTargetColumn === column.key && draggingColumn !== column.key
          }"
          :data-status="column.key"
        >
        <header class="column-header">
          <div class="column-title">
            <span class="dot" :style="{ background: column.color }" />
            <strong>{{ column.title }}</strong>
          </div>
          <div class="column-header-right">
            <span class="column-count">{{ column.items.length }}</span>
            <el-tooltip content="新建缺陷" placement="top">
              <el-button
                type="primary"
                circle
                plain
                size="small"
                class="create-button"
                @click.stop="handleCreateDefect(column.key)"
              >
                <el-icon><Plus /></el-icon>
              </el-button>
            </el-tooltip>
          </div>
        </header>

        <el-scrollbar class="column-scroll">
          <Draggable
            :list="column.items"
            item-key="id"
            class="column-scroll-list"
            :class="{ 'drag-over': isDragging && dragTargetColumn === column.key && draggingColumn !== column.key }"
            :component-data="{ attrs: { 'data-status': column.key } }"
            group="defect-status"
            :animation="150"
            :disabled="props.loading || (updatingDefectId !== null && (updatingDefectStatus === column.key || draggingColumn === column.key)) || column.disabled"
            ghost-class="kanban-ghost"
            chosen-class="kanban-chosen"
            drag-class="kanban-drag"
            :force-fallback="true"
            :fallback-tolerance="5"
            :scroll="true"
            :scroll-sensitivity="80"
            :scroll-speed="15"
            :delay="0"
            :delay-on-touch-only="true"
            :touch-start-threshold="5"
            :empty-insert-threshold="10"
            @start="(event: any) => handleDragStart(event, column.key)"
            @end="(event: any) => handleDragEnd(event)"
            @add="handleDragAdd"
            @change="handleDragChange"
            @update="handleDragUpdate"
            @move="handleDragMove"
          >
            <template #item="{ element }">
              <el-card
                :data-defect-id="element.id"
                :data-id="element.id"
                shadow="never"
                class="kanban-card"
                :class="{ 
                  'is-dragging': isDragging && draggingDefectId === element.id,
                  'is-updating': updatingDefectId === element.id
                }"
                @mousedown="handleCardMouseDown(element.id, $event)"
                @click.stop="handleCardClick(element.id, $event)"
              >
                <div class="card-header">
                  <div class="card-id-wrapper">
                    <span class="card-id">#{{ element.id.slice(-8) }}</span>
                    <el-tag 
                      v-if="element.type" 
                      size="small" 
                      type="info"
                      class="type-tag"
                    >
                      {{ getTypeText(element.type) }}
                    </el-tag>
                  </div>
                  <div class="priority-severity">
                    <el-tag size="small" :type="priorityTagType(element.priority)" class="priority-tag">
                      {{ getPriorityLabel(element.priority) }}
                    </el-tag>
                    <el-tag size="small" :type="severityTagType(element.severity)" class="severity-tag">
                      {{ getSeverityLabel(element.severity) }}
                    </el-tag>
                  </div>
                </div>
                <div class="card-title" :title="element.title">{{ element.title }}</div>
                <div class="card-meta">
                  <div class="meta-item" v-if="element.assignee_name">
                    <el-icon class="meta-icon"><User /></el-icon>
                    <span class="meta-text">{{ element.assignee_name }}</span>
                  </div>
                  <div class="meta-item" v-else>
                    <el-icon class="meta-icon"><UserFilled /></el-icon>
                    <span class="meta-text text-muted">未分配</span>
                  </div>
                  <div class="meta-item" v-if="element.project_name">
                    <el-icon class="meta-icon"><Folder /></el-icon>
                    <span class="meta-text">{{ element.project_name }}</span>
                  </div>
                </div>
                <div class="card-footer">
                  <span class="time">
                    <el-icon class="time-icon"><Clock /></el-icon>
                    {{ formatDate(element.updated_at) }}
                  </span>
                  <div class="card-actions">
                    <el-button 
                      type="primary" 
                      text 
                      size="small" 
                      class="action-button"
                      @click.stop="$emit('open-edit', element.id)"
                    >
                      <el-icon><Edit /></el-icon>
                    </el-button>
                    <el-button 
                      type="danger" 
                      text 
                      size="small" 
                      class="action-button delete-button"
                      @click.stop="handleDeleteClick(element)"
                    >
                      <el-icon><Delete /></el-icon>
                    </el-button>
                  </div>
                </div>
              </el-card>
            </template>
          </Draggable>
        </el-scrollbar>
        </div>
      </div>
    </el-scrollbar>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, User, UserFilled, Folder, Clock, Edit, Delete } from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import Draggable from 'vuedraggable'
import type { Defect } from '@/api/defect'
import { deleteDefect } from '@/api/defect'
import { getDefectTypeLabel } from '@/types/defect'
import { useDefectOptions } from '@/composables/useDefectOptions'
import { ResponseParser } from '@/utils/responseParser'

const props = defineProps<{
  defects: Defect[]
  statusOptions: Array<{ value: string; label: string; color?: string; disabled?: boolean }>
  priorityOptions: Array<{ value: string; label: string }>
  severityOptions: Array<{ value: string; label: string }>
  typeOptions: Array<{ value: string; label: string }>
  loading: boolean
}>()

const emit = defineEmits<{
  (event: 'open-detail', id: string): void
  (event: 'open-edit', id: string): void
  (event: 'update-status', payload: { id: string; status: string }): void
  (event: 'create-defect', status: string): void
  (event: 'deleted', id: string): void
}>()

const deletingIds = ref<Set<string>>(new Set())
const isDragging = ref(false)
const draggingDefectId = ref<string | null>(null)
const draggingColumn = ref<string | null>(null)
const dragTargetColumn = ref<string | null>(null) // 当前拖拽目标列
const updatingDefectId = ref<string | null>(null)
const updatingDefectStatus = ref<string | null>(null) // 正在更新的缺陷所在的状态列

// 用于区分点击和拖动
let dragStartTime = 0
let dragStartPosition = { x: 0, y: 0 }
let hasDragged = false

const recycleColumn = {
  key: 'unclassified',
  title: '未分类',
  color: '#c9ccd8',
  disabled: false
}

// 使用 composables 提供的函数
const { getPriorityName: getDefectPriorityNameFromComposable, getSeverityName: getDefectSeverityNameFromComposable, getPriorityType: getDefectPriorityTypeFromComposable, getSeverityType: getDefectSeverityTypeFromComposable } = useDefectOptions()

const getPriorityLabel = (value?: string | null) => {
  if (!value) return '未设置'
  // 优先使用 composables 提供的函数
  const priorityName = getDefectPriorityNameFromComposable(value)
  if (priorityName && priorityName !== value) return priorityName
  // 回退到 props 中的选项
  return props.priorityOptions.find(option => option.value === value)?.label ?? value
}

const getSeverityLabel = (value?: string | null) => {
  if (!value) return '未设置'
  // 优先使用 composables 提供的函数
  const severityName = getDefectSeverityNameFromComposable(value)
  if (severityName && severityName !== value) return severityName
  // 回退到 props 中的选项
  return props.severityOptions.find(option => option.value === value)?.label ?? value
}

const getTypeText = (type?: string | null) => {
  if (!type) return ''
  return getDefectTypeLabel(type) || props.typeOptions.find(option => option.value === type)?.label || type
}

const priorityTagType = (priority?: string | null) => {
  if (!priority) return 'info'
  return getDefectPriorityTypeFromComposable(priority)
}

const severityTagType = (severity?: string | null) => {
  if (!severity) return 'info'
  return getDefectSeverityTypeFromComposable(severity)
}

const formatDate = (value?: string) => {
  if (!value) return '—'
  return dayjs(value).format('MM-DD HH:mm')
}

const columns = computed(() => {
  const map = new Map<string, Defect[]>()
  props.statusOptions.forEach(option => map.set(option.value, []))

  const fallback: Defect[] = []

  props.defects.forEach(item => {
    if (item.status && map.has(item.status)) {
      map.get(item.status)?.push(item)
    } else {
      fallback.push(item)
    }
  })

  const result = props.statusOptions.map(option => ({
    key: option.value,
    title: option.label,
    color: option.color || '#528bff',
    items: map.get(option.value) || [],
    disabled: option.disabled || false // 支持状态级别的禁用
  }))

  if (fallback.length) {
    result.push({
      ...recycleColumn,
      items: fallback
    })
  }

  return result
})

const handleCreateDefect = (status: string) => {
  emit('create-defect', status)
}

// 处理删除点击
async function handleDeleteClick(defect: Defect) {
  if (!defect.id) {
    return
  }

  // 如果正在删除，直接返回
  if (deletingIds.value.has(defect.id)) {
    return
  }

  try {
    await ElMessageBox.confirm(
      `确定要删除缺陷"${defect.title}"吗？此操作不可恢复。`,
      '删除确认',
      {
        confirmButtonText: '确定删除',
        cancelButtonText: '取消',
        type: 'warning',
        confirmButtonClass: 'el-button--danger',
        beforeClose: async (action, instance, done) => {
          if (action === 'confirm') {
            instance.confirmButtonLoading = true
            try {
              await performDelete(defect.id!)
              done()
            } catch (error) {
              instance.confirmButtonLoading = false
            }
          } else {
            done()
          }
        }
      }
    )
  } catch (error) {
    // 用户取消删除
    if (error !== 'cancel') {
      console.error('删除确认对话框错误:', error)
    }
  }
}

// 执行删除操作
async function performDelete(defectId: string) {
  if (deletingIds.value.has(defectId)) {
    return
  }

  deletingIds.value.add(defectId)
  try {
    const response = await deleteDefect(defectId)
    const parsed = ResponseParser.parseResponse(response)
    
    if (!parsed.success) {
      throw new Error(parsed.message || '删除失败')
    }

    ElMessage.success('删除成功')
    emit('deleted', defectId)
  } catch (error) {
    const message = ResponseParser.extractMessage(error) || '删除失败'
    ElMessage.error(message)
    throw error
  } finally {
    deletingIds.value.delete(defectId)
  }
}

// 处理卡片鼠标按下事件，记录初始位置
const handleCardMouseDown = (_defectId: string, event: MouseEvent) => {
  // 如果点击的是按钮等交互元素，不记录位置
  const target = event.target as HTMLElement
  if (target.closest('.action-button') || target.closest('button') || target.closest('a')) {
    return
  }
  
  dragStartTime = Date.now()
  dragStartPosition.x = event.clientX
  dragStartPosition.y = event.clientY
  hasDragged = false
}

// 处理卡片点击事件，区分点击和拖动
const handleCardClick = (defectId: string, event: MouseEvent) => {
  // 如果点击的是按钮等交互元素，不处理
  const target = event.target as HTMLElement
  if (target.closest('.action-button') || target.closest('button') || target.closest('a')) {
    return
  }
  
  // 如果正在拖动或已经拖动过，不触发点击事件
  if (isDragging.value || hasDragged) {
    // 延迟重置，确保拖动事件处理完成
    setTimeout(() => {
      hasDragged = false
    }, 50)
    return
  }
  
  // 检查是否是拖动操作（移动距离超过5px或时间超过200ms）
  const timeDiff = Date.now() - dragStartTime
  const distance = Math.sqrt(
    Math.pow(event.clientX - dragStartPosition.x, 2) + 
    Math.pow(event.clientY - dragStartPosition.y, 2)
  )
  
  // 如果移动距离很小且时间很短，认为是点击
  if (distance < 5 && timeDiff < 300) {
    emit('open-detail', defectId)
  }
}

// 节流函数，用于优化拖动事件处理
let dragUpdateTimer: ReturnType<typeof setTimeout> | null = null
const throttleDragUpdate = (callback: () => void, delay = 30) => {
  if (dragUpdateTimer) return
  dragUpdateTimer = setTimeout(() => {
    callback()
    dragUpdateTimer = null
  }, delay)
}


const handleDragStart = (event: any, columnKey: string) => {
  // 检查是否被禁用
  const column = columns.value.find(col => col.key === columnKey)
  if (column?.disabled) {
    console.warn('列被禁用，无法拖动:', columnKey, column)
    return
  }
  
  if (props.loading || updatingDefectId.value !== null) {
    console.warn('拖动被禁用:', { loading: props.loading, updatingDefectId: updatingDefectId.value })
    return
  }
  
  // 标记已经开始拖动
  hasDragged = true
  
  // 记录拖动开始时间和位置
  dragStartTime = Date.now()
  if (event?.originalEvent) {
    dragStartPosition.x = event.originalEvent.clientX || 0
    dragStartPosition.y = event.originalEvent.clientY || 0
  } else if (event?.touches?.[0]) {
    dragStartPosition.x = event.touches[0].clientX || 0
    dragStartPosition.y = event.touches[0].clientY || 0
  } else if (event?.item) {
    // 尝试从 item 元素获取位置
    const rect = event.item.getBoundingClientRect()
    dragStartPosition.x = rect.left + rect.width / 2
    dragStartPosition.y = rect.top + rect.height / 2
  }
  
  // 设置拖拽状态
  isDragging.value = true
  draggingColumn.value = columnKey
  dragTargetColumn.value = null
  
  // 获取拖拽的缺陷ID
  const { item } = event || {}
  if (item) {
    const cardElement = item.querySelector?.('.kanban-card') || item
    draggingDefectId.value = cardElement.getAttribute('data-defect-id') ||
      cardElement.getAttribute('data-id') ||
      cardElement.dataset?.defectId ||
      cardElement.dataset?.id ||
      null
  }
  
  // 添加拖拽状态类，用于全局样式控制
  document.body.classList.add('dragging')
  // 添加拖拽时的样式，禁用文本选择
  document.body.style.userSelect = 'none'
  document.body.style.cursor = 'grabbing'
  
  console.log('拖动开始:', { columnKey, defectId: draggingDefectId.value, columns: columns.value.map(c => ({ key: c.key, disabled: c.disabled })) })
}

const handleDragAdd = (event: any) => {
  // 拖拽添加到新列时的处理（用于视觉反馈）
  const { to } = event || {}
  if (to) {
    const targetColumn = to.closest?.('.kanban-column') as HTMLElement | null
    if (targetColumn) {
      const status = targetColumn.getAttribute('data-status') || targetColumn.dataset?.status
      if (status) {
        dragTargetColumn.value = status
      }
    }
  }
}

const handleDragChange = () => {
  // 拖拽位置变化时的处理（用于视觉反馈）
}

const handleDragUpdate = () => {
  // 拖拽更新时的处理
}

const handleDragMove = (event: any) => {
  // 拖拽移动时的处理，实时更新目标列
  // 使用节流优化性能
  throttleDragUpdate(() => {
    const { related, dragged, willInsertAfter: _willInsertAfter } = event || {}
    
    // 方法1: 从 related 元素获取目标列（最可靠）
    if (related) {
      const targetColumn = related.closest?.('.kanban-column') as HTMLElement | null
      if (targetColumn) {
        const status = targetColumn.getAttribute('data-status') || targetColumn.dataset?.status
        if (status && status !== draggingColumn.value) {
          dragTargetColumn.value = status
          return
        }
      }
    }
    
    // 方法2: 从 dragged 元素的当前父级获取
    if (dragged && dragged.parentElement) {
      const targetColumn = dragged.parentElement.closest?.('.kanban-column') as HTMLElement | null
      if (targetColumn) {
        const status = targetColumn.getAttribute('data-status') || targetColumn.dataset?.status
        if (status && status !== draggingColumn.value) {
          dragTargetColumn.value = status
          return
        } else if (status === draggingColumn.value) {
          dragTargetColumn.value = null
          return
        }
      }
    }
    
    // 方法3: 如果无法确定目标，清空目标列
    dragTargetColumn.value = null
  }, 30)
}

const handleDragEnd = async (event: any) => {
  // 清理节流定时器
  if (dragUpdateTimer) {
    clearTimeout(dragUpdateTimer)
    dragUpdateTimer = null
  }
  
  // 移除拖拽状态类
  document.body.classList.remove('dragging')
  document.body.style.userSelect = ''
  document.body.style.cursor = ''
  
  // 重置拖拽状态
  const currentDraggingId = draggingDefectId.value
  
  isDragging.value = false
  draggingColumn.value = null
  dragTargetColumn.value = null
  
  // 延迟清除拖拽的缺陷ID和拖动标记，确保事件处理完成
  setTimeout(() => {
    if (draggingDefectId.value === currentDraggingId) {
      draggingDefectId.value = null
    }
    hasDragged = false
  }, 100)
  
  const { item, from, to, oldIndex, newIndex } = event || {}
  
  if (!item || !from || !to) {
    return
  }
  
  // 从 DOM 元素获取目标状态（查找最近的 .kanban-column）
  const targetColumn = to.closest?.('.kanban-column') || to.parentElement?.closest?.('.kanban-column')
  const finalTargetStatus = targetColumn?.getAttribute('data-status') || 
    targetColumn?.dataset?.status ||
    to?.getAttribute('data-status') ||
    to?.dataset?.status
  
  // 获取源状态
  const sourceColumn = from.closest?.('.kanban-column') || from.parentElement?.closest?.('.kanban-column')
  const sourceStatus = sourceColumn?.getAttribute('data-status') || 
    sourceColumn?.dataset?.status ||
    from?.getAttribute('data-status') ||
    from?.dataset?.status
  
  // 检查状态是否真的改变了
  if (finalTargetStatus === sourceStatus) {
    // 同一列内移动，不需要更新状态
    return
  }
  
  if (!finalTargetStatus) {
    console.warn('无法获取目标状态', { targetColumn, to, event })
    // 恢复原始位置
    if (oldIndex !== undefined && from) {
      const children = from.children
      if (children[oldIndex]) {
        from.insertBefore(item, children[oldIndex])
      } else if (oldIndex === 0) {
        from.insertBefore(item, children[0])
      } else {
        from.appendChild(item)
      }
    }
    return
  }
  
  // 获取缺陷ID - 优先使用保存的拖拽ID，然后尝试其他方法
  let defectId = currentDraggingId || item.getAttribute?.('data-defect-id') ||
    item.dataset?.defectId ||
    item.getAttribute?.('data-id') ||
    item.dataset?.id
  
  // 方法2: 从卡片的子元素中查找
  if (!defectId) {
    const cardElement = item.querySelector?.('.kanban-card')
    if (cardElement) {
      defectId = cardElement.getAttribute('data-defect-id') ||
        cardElement.getAttribute('data-id') ||
        cardElement.dataset?.defectId ||
        cardElement.dataset?.id
    }
  }
  
  // 方法3: 尝试从 event.itemContext 或 item.__draggable_context 获取
  if (!defectId && event?.itemContext) {
    defectId = event.itemContext.element?.id
  }
  
  if (!defectId && item.__draggable_context) {
    defectId = item.__draggable_context.element?.id
  }
  
  // 方法4: 作为最后手段，从目标列的 items 数组中根据 newIndex 获取
  if (!defectId && typeof newIndex === 'number' && newIndex >= 0) {
    const targetColumnData = columns.value.find(col => col.key === finalTargetStatus)
    if (targetColumnData && targetColumnData.items[newIndex]) {
      defectId = targetColumnData.items[newIndex].id
    }
  }
  
  if (!defectId) {
    console.warn('拖拽事件缺少缺陷ID:', { item, from, to, oldIndex, newIndex, event })
    // 恢复原始位置
    if (oldIndex !== undefined && from) {
      const children = from.children
      if (children[oldIndex]) {
        from.insertBefore(item, children[oldIndex])
      } else if (oldIndex === 0) {
        from.insertBefore(item, children[0])
      } else {
        from.appendChild(item)
      }
    }
    ElMessage.error('无法获取缺陷信息，请重试')
    return
  }

  // 设置更新状态，防止重复拖拽
  if (updatingDefectId.value === defectId) {
    return
  }
  
  updatingDefectId.value = defectId
  updatingDefectStatus.value = sourceStatus // 记录源状态，只禁用源状态列和目标状态列
  
  // 传递对象参数，匹配 emit 类型定义
  try {
    emit('update-status', { id: defectId, status: finalTargetStatus })
    // 延迟清除更新状态，确保更新完成
    setTimeout(() => {
      if (updatingDefectId.value === defectId) {
        updatingDefectId.value = null
        updatingDefectStatus.value = null
      }
    }, 1000)
  } catch (error) {
    updatingDefectId.value = null
    updatingDefectStatus.value = null
    throw error
  }
}
</script>

<style scoped>
.kanban-container {
  min-height: 0;
  min-width: 0;
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.kanban-scroll {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  height: 100%;
}

.kanban-scroll :deep(.el-scrollbar) {
  height: 100%;
}

.kanban-scroll :deep(.el-scrollbar__wrap) {
  overflow-y: hidden;
  overflow-x: auto;
  height: 100%;
}

.kanban-board {
  display: flex;
  gap: 16px;
  padding-bottom: 4px;
  min-height: 100%;
  width: max-content;
}

.kanban-column {
  display: flex;
  flex-direction: column;
  flex: 0 0 280px;
  height: calc(100vh - 280px);
  min-height: 400px;
  max-height: calc(100vh - 200px);
  border-radius: 16px;
  background: linear-gradient(180deg, #ffffff 0%, #f4f7ff 100%);
  box-shadow: inset 0 0 0 1px rgba(91, 118, 184, 0.12);
  overflow: hidden;
}

.column-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 16px;
  border-bottom: 1px solid rgba(102, 126, 185, 0.12);
}

.column-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: #264074;
}

.column-title .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.column-header-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.column-count {
  font-size: 12px;
  color: #7582a4;
}

.create-button {
  flex-shrink: 0;
}

.column-scroll {
  flex: 1;
  padding: 12px 12px 6px;
  min-height: 0;
  height: 100%;
  overflow: hidden;
}

.column-scroll :deep(.el-scrollbar) {
  height: 100%;
}

.column-scroll :deep(.el-scrollbar__wrap) {
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto !important;
}

.column-scroll :deep(.el-scrollbar__view) {
  min-height: auto;
}

.column-scroll :deep(.el-scrollbar__bar) {
  right: 2px;
}

.column-scroll :deep(.el-scrollbar__bar.is-vertical) {
  width: 6px;
}

.column-scroll :deep(.el-scrollbar__thumb) {
  background-color: rgba(144, 147, 153, 0.3);
  border-radius: 3px;
}

.column-scroll :deep(.el-scrollbar__thumb:hover) {
  background-color: rgba(144, 147, 153, 0.5);
}

.column-scroll-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: auto;
  transition: all 0.2s ease;
}

.column-scroll-list.drag-over {
  background-color: rgba(64, 158, 255, 0.08);
  border-radius: 8px;
  position: relative;
}

.column-scroll-list.drag-over::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border: 2px dashed rgba(64, 158, 255, 0.5);
  border-radius: 8px;
  pointer-events: none;
  animation: drag-over-border 1s ease-in-out infinite;
}

@keyframes drag-over-border {
  0%, 100% {
    border-color: rgba(64, 158, 255, 0.5);
    opacity: 1;
  }
  50% {
    border-color: rgba(64, 158, 255, 0.8);
    opacity: 0.8;
  }
}

.kanban-column.drag-target {
  background: linear-gradient(180deg, rgba(64, 158, 255, 0.12) 0%, rgba(244, 247, 255, 0.95) 100%);
  box-shadow: inset 0 0 0 3px rgba(64, 158, 255, 0.4), 0 4px 16px rgba(64, 158, 255, 0.2);
  border-radius: 16px;
  transform: scale(1.02) translateZ(0);
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  animation: drag-target-pulse 1.5s ease-in-out infinite;
  will-change: transform, box-shadow;
  backface-visibility: hidden;
}

.kanban-column.drag-over {
  position: relative;
}

.kanban-column.drag-over::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(180deg, rgba(64, 158, 255, 0.1) 0%, rgba(64, 158, 255, 0.05) 100%);
  border-radius: 16px;
  pointer-events: none;
  z-index: 1;
  animation: drag-over-glow 1s ease-in-out infinite;
  will-change: opacity;
  backface-visibility: hidden;
}

@keyframes drag-target-pulse {
  0%, 100% {
    box-shadow: inset 0 0 0 3px rgba(64, 158, 255, 0.4), 0 4px 16px rgba(64, 158, 255, 0.2);
  }
  50% {
    box-shadow: inset 0 0 0 3px rgba(64, 158, 255, 0.6), 0 6px 20px rgba(64, 158, 255, 0.3);
  }
}

@keyframes drag-over-glow {
  0%, 100% {
    opacity: 0.6;
  }
  50% {
    opacity: 1;
  }
}

.kanban-column.drag-source {
  opacity: 0.85;
  transition: opacity 0.2s ease;
}

.kanban-ghost {
  opacity: 0.4 !important;
  background: #f5f7fa !important;
  border: 2px dashed #409eff !important;
  border-radius: 12px !important;
  transform: scale(0.95);
  transition: all 0.2s ease;
}

.kanban-chosen {
  cursor: grabbing !important;
  opacity: 0.9;
  transform: rotate(2deg) scale(1.02);
  z-index: 1000;
  transition: all 0.2s ease;
}

.kanban-drag {
  opacity: 0.8 !important;
  transform: rotate(3deg) scale(1.05) translateZ(0) !important;
  box-shadow: 0 8px 24px rgba(64, 158, 255, 0.3) !important;
  border-color: #409eff !important;
  z-index: 1001 !important;
  cursor: grabbing !important;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
  will-change: transform;
  backface-visibility: hidden;
  pointer-events: none;
}

.kanban-card.is-dragging {
  opacity: 0.5;
  pointer-events: none;
  transform: scale(0.95);
  transition: all 0.2s ease;
}

.kanban-card.is-updating {
  position: relative;
  pointer-events: none;
  opacity: 0.7;
}

.kanban-card.is-updating::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.8);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.kanban-card.is-updating::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -10px;
  margin-left: -10px;
  width: 20px;
  height: 20px;
  border: 2px solid #409eff;
  border-top-color: transparent;
  border-radius: 50%;
  animation: kanban-spin 0.8s linear infinite;
  z-index: 11;
}

@keyframes kanban-spin {
  to {
    transform: rotate(360deg);
  }
}

.kanban-card {
  margin-bottom: 12px;
  border-radius: 12px;
  cursor: grab;
  border: 1px solid #e4e7ed;
  background: #ffffff;
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  position: relative;
  overflow: hidden;
  user-select: none;
  -webkit-user-drag: element;
  transform: translateZ(0);
  will-change: transform;
  backface-visibility: hidden;
}

.kanban-card:active {
  cursor: grabbing;
}

.kanban-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 3px;
  height: 100%;
  background: linear-gradient(180deg, #409eff 0%, #66b1ff 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.kanban-card:hover:not(.is-dragging):not(.is-updating) {
  transform: translateY(-2px);
  border-color: #409eff;
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}

.kanban-card:hover:not(.is-dragging):not(.is-updating)::before {
  opacity: 1;
}

.kanban-card:active:not(.is-dragging):not(.is-updating) {
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}

.kanban-card :deep(.el-card__body) {
  padding: 14px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 10px;
  gap: 8px;
}

.card-id-wrapper {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
}

.card-id {
  font-size: 11px;
  color: #909399;
  font-weight: 500;
  font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
  letter-spacing: 0.5px;
}

.type-tag {
  font-size: 10px;
  padding: 0 6px;
  height: 18px;
  line-height: 18px;
}

.priority-severity {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex-shrink: 0;
}

.priority-tag,
.severity-tag {
  font-size: 11px;
  padding: 0 8px;
  height: 20px;
  line-height: 20px;
  font-weight: 500;
}

.card-title {
  font-weight: 600;
  font-size: 14px;
  color: #303133;
  margin-bottom: 12px;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-word;
  min-height: 42px;
  transition: color 0.2s ease;
}

.kanban-card:hover .card-title {
  color: #409eff;
}

.card-meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
  padding-top: 8px;
  border-top: 1px solid #f0f2f5;
}

.meta-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #606266;
}

.meta-icon {
  font-size: 14px;
  color: #909399;
  flex-shrink: 0;
}

.meta-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.meta-text.text-muted {
  color: #c0c4cc;
}

.card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 10px;
  border-top: 1px solid #f0f2f5;
}

.time {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: #909399;
}

.time-icon {
  font-size: 12px;
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

.action-button {
  padding: 4px 8px;
  opacity: 0.7;
  transition: all 0.2s ease;
}

.kanban-card:hover .action-button {
  opacity: 1;
}

.action-button:hover {
  opacity: 1;
  background-color: #ecf5ff;
}

.action-button:not(.delete-button):hover {
  color: #409eff;
}

.delete-button:hover {
  color: #f56c6c;
  background-color: #fef0f0;
}
</style>

